/*
* SDL++ is a C++ wrapper for SDL2 (www.libsdl.org/).
*
* @author: dzlua
* @contact: 505544956@qq.com
* @date: August 17, 2020
* @example: https://gitee.com/dzlua/sdl.git
*/

#ifndef __SDL_HPP_
#define __SDL_HPP_

#include <utility>
#include <SDL.h>
#include <SDL_opengl.h>

#ifdef SDLPP_USE_ALL
  #define SDLPP_USE_IMAGE
  #define SDLPP_USE_TTF
  #define SDLPP_USE_MIXER
#endif // SDLPP_USE_ALL

#ifdef SDLPP_USE_IMAGE
  #include <SDL_image.h>
#endif // SDLPP_USE_IMAGE
#ifdef SDLPP_USE_TTF
  #include <SDL_ttf.h>
#endif // SDLPP_USE_TTF
#ifdef SDLPP_USE_MIXER
  #include <SDL_mixer.h>
#endif // SDLPP_USE_MIXER

namespace sdl {

// Warpper a C style class to c++ class.
// https://gitee.com/dzlua/test/blob/master/WrapperC/wrapper.hpp
template <typename T, typename C>
class Wrapper {
  public:
    Wrapper(C *cptr = nullptr) noexcept { reset(cptr); }
    Wrapper(const Wrapper<T,C>& r) noexcept { reset(r.get()); }
    Wrapper<T,C>& operator=(const Wrapper<T,C>& r) noexcept { reset(r.get()); return *this; }
    Wrapper<T,C>& operator=(C *cptr) noexcept { reset(cptr); return *this; }
  public:
    inline T* operator->() noexcept { return &obj_; }
    inline operator C*() const noexcept { return get(); }
    inline explicit operator bool() const noexcept { return get() != nullptr; }
  public:
    inline C* get() const noexcept { return obj_.get(); }
    inline C*& set() noexcept { return obj_._set(); }
    inline void reset(C *cptr = nullptr) noexcept { obj_.reset(cptr); }
    inline void swap(Wrapper<T,C>& r) noexcept { obj_.swap(r.obj_); }
  protected:
    T obj_;
};

template <typename T, typename C>
inline bool operator==(const Wrapper<T,C>& l, const Wrapper<T,C>& r) noexcept { return l.get() == r.get(); }
template <typename T, typename C>
inline bool operator==(const Wrapper<T,C>& l, std::nullptr_t) noexcept { return l.get() == nullptr; }
template <typename T, typename C>
inline bool operator==(std::nullptr_t, const Wrapper<T,C>& r) noexcept { return nullptr == r.get(); }
template <typename T, typename C>
inline bool operator!=(const Wrapper<T,C>& l, const Wrapper<T,C>& r) noexcept { return l.get() != r.get(); }
template <typename T, typename C>
inline bool operator!=(const Wrapper<T,C>& l, std::nullptr_t) noexcept { return l.get() != nullptr; }
template <typename T, typename C>
inline bool operator!=(std::nullptr_t, const Wrapper<T,C>& r) noexcept { return nullptr != r.get(); }

template <typename T, typename C>
class ClassWrapped {
    friend class Wrapper<T,C>;
  protected:
    ClassWrapped() : cptr_(nullptr) {}
    virtual ~ClassWrapped() { reset(nullptr); }
    inline C* get() const noexcept { return cptr_; }
    inline C*& _set() noexcept { return cptr_; }
    inline void reset(C *cptr = nullptr) noexcept { cptr_ = cptr; }
    inline void swap(ClassWrapped<T,C>& r) noexcept { std::swap(cptr_, r.cptr_); }
  protected:
    C* cptr_;
};

// macros for wrapper sdl.
#define SDLPP_WRAPPER_CLASS_BEGIN(classname,sdlstruct) \
  class classname : public ClassWrapped<classname, sdlstruct> { \
      friend class Wrapper<classname,sdlstruct>; \
    protected: \
      classname() = default; \
    public: \
      typedef Wrapper<classname,sdlstruct> Wrapped;
#define SDLPP_WRAPPER_CLASS_END };

// base type
typedef int result;
typedef SDL_bool Bool;

typedef SDL_version Version;

namespace display {
  typedef SDL_DisplayMode Mode;
  typedef SDL_DisplayOrientation Orientation;
}

typedef SDL_BlendMode BlendMode;
typedef SDL_BlendOperation BlendOperation;
typedef SDL_BlendFactor BlendFactor;

typedef SDL_Palette Palette;
typedef SDL_PixelFormat PixelFormat;

inline Bool isSucceeded(result rst) { return (rst >= 0) ? SDL_TRUE : SDL_FALSE; }
inline Bool isFailed(result rst) { return (rst < 0) ? SDL_TRUE : SDL_FALSE; }

struct Point : public SDL_Point {
  Point() { reset(); }
  inline void reset() { x = y = 0; }
};

struct FPoint : public SDL_FPoint {
  FPoint() { reset(); }
  inline void reset() { x = y = 0.f; }
};

struct Size {
  int w, h;
  Size() : w(0), h(0) {}
  inline void reset() { w = h = 0; }
};

struct FSize {
  float w, h;
  FSize() : w(0.f), h(0.f) {}
  inline void reset() { w = h = 0.f; }
};

struct Rect : public SDL_Rect {
  Rect() { reset(); }
  Rect(const Point &pt) { w = h = 0; x = pt.x; y = pt.y; }
  Rect(const Size &sz) { x = y = 0; w = sz.w; h = sz.h; }
  Rect(const Point &pt, const Size &sz) { x = pt.x; y = pt.y; w = sz.w; h = sz.h; }
  inline void reset() { x = y = w = h = 0; }
};

struct FRect : public SDL_FRect {
  FRect() { reset(); }
  FRect(const FPoint &pt) { w = h = 0.f; x = pt.x; y = pt.y; }
  FRect(const FSize &sz) { x = y = 0.f; w = sz.w; h = sz.h; }
  FRect(const FPoint &pt, const FSize &sz) { x = pt.x; y = pt.y; w = sz.w; h = sz.h; }
  inline void reset() { x = y = w = h = 0.f; }
};

// RWops
SDLPP_WRAPPER_CLASS_BEGIN(RWops, SDL_RWops)
  enum class Seek : Sint8 {
    Set = 0, Cur = 1,  End = 2
  };

  static inline Wrapped fromFile(const char *file, const char *mode) {
    return Wrapped(SDL_RWFromFile(file, mode)); }
  #ifdef HAVE_STDIO_H
    static inline Wrapped fromFP(FILE * fp, Bool autoclose) {
      return Wrapped(SDL_RWFromFP(fp, autoclose)); }
  #else
    static inline Wrapped fromFP(void * fp, Bool autoclose) {
      return Wrapped(SDL_RWFromFP(fp, autoclose)); }
  #endif
  static inline Wrapped fromMem(void *mem, int size) {
    return Wrapped(SDL_RWFromMem(mem, size)); }
  static inline Wrapped fromMem(const void *mem, int size) {
    return Wrapped(SDL_RWFromConstMem(mem, size)); }
  static inline Wrapped alloc() { return Wrapped(SDL_AllocRW()); }

  inline void free() { SDL_FreeRW(get()); }
  inline Sint64 size() { return SDL_RWsize(get()); }
  inline Sint64 seek(Sint64 offset, Seek whence) { return SDL_RWseek(get(), offset, (int)whence); }
  inline Sint64 tell() { return SDL_RWtell(get()); }
  inline size_t read(void *ptr, size_t size, size_t maxnum) { return SDL_RWread(get(), ptr, size, maxnum); }
  inline size_t write(const void *ptr, size_t size, size_t num) { return SDL_RWwrite(get(), ptr, size, num); }
  inline result close() { return SDL_RWclose(get()); }
  inline void* loadFile(size_t *datasize, Bool freesrc) { return SDL_LoadFile_RW(get(), datasize, freesrc); }
  static inline void* loadFile(const char *file, size_t *datasize) { return SDL_LoadFile(file, datasize); }
  inline Uint8 readU8() { return SDL_ReadU8(get()); }
  inline Uint16 readLE16() { return SDL_ReadLE16(get()); }
  inline Uint16 readBE16() { return SDL_ReadBE16(get()); }
  inline Uint32 readLE32() { return SDL_ReadLE32(get()); }
  inline Uint32 readBE32() { return SDL_ReadBE32(get()); }
  inline Uint64 readLE64() { return SDL_ReadLE64(get()); }
  inline Uint64 readBE64() { return SDL_ReadBE64(get()); }
  inline size_t writeU8(Uint8 value) { return SDL_WriteU8(get(), value); }
  inline size_t writeLE16(Uint16 value) { return SDL_WriteLE16(get(), value); }
  inline size_t writeBE16(Uint16 value) { return SDL_WriteBE16(get(), value); }
  inline size_t writeLE32(Uint32 value) { return SDL_WriteLE32(get(), value); }
  inline size_t writeBE32(Uint32 value) { return SDL_WriteBE32(get(), value); }
  inline size_t writeLE64(Uint64 value) { return SDL_WriteLE64(get(), value); }
  inline size_t writeBE64(Uint64 value) { return SDL_WriteBE64(get(), value); }
SDLPP_WRAPPER_CLASS_END

// texture
SDLPP_WRAPPER_CLASS_BEGIN(Texture, SDL_Texture)
  typedef SDL_TextureModulate Modulate;
  typedef SDL_TextureAccess Access;
  typedef SDL_ScaleMode ScaleMode;

  inline result query(Uint32 *format, int *access, int *w, int *h) {
    return SDL_QueryTexture(get(), format, access, w, h); }
  inline result setColorMod(Uint8 r, Uint8 g, Uint8 b) { return SDL_SetTextureColorMod(get(), r, g, b); }
  inline result getColorMod(Uint8 *r, Uint8 *g, Uint8 *b) { return SDL_GetTextureColorMod(get(), r, g, b); }
  inline result setAlphaMod(Uint8 alpha) { return SDL_SetTextureAlphaMod(get(), alpha); }
  inline result getAlphaMod(Uint8 *alpha) { return SDL_GetTextureAlphaMod(get(), alpha); }
  inline result setBlendMode(BlendMode blendMode) { return SDL_SetTextureBlendMode(get(), blendMode); }
  inline result getBlendMode(BlendMode *blendMode) { return SDL_GetTextureBlendMode(get(), blendMode); }
  inline result setBlendMode(ScaleMode scaleMode) { return SDL_SetTextureScaleMode(get(), scaleMode); }
  inline result getBlendMode(ScaleMode *scaleMode) { return SDL_GetTextureScaleMode(get(), scaleMode); }
  inline result update(const SDL_Rect *rect, const void *pixels, int pitch) {
    return SDL_UpdateTexture(get(), rect, pixels, pitch); }
  inline result updateYUV(const SDL_Rect *rect,
      const Uint8 *Yplane, int Ypitch,
      const Uint8 *Uplane, int Upitch,
      const Uint8 *Vplane, int Vpitch) {
    return SDL_UpdateYUVTexture(get(), rect, Yplane, Ypitch, Uplane, Upitch, Vplane, Vpitch); }
  inline result lock(const SDL_Rect *rect, void **pixels, int *pitch) {
    return SDL_LockTexture(get(), rect, pixels, pitch); }
  inline result lockToSurface(const SDL_Rect *rect, SDL_Surface **surface) {
    return SDL_LockTextureToSurface(get(), rect, surface); }
  inline void unlock() { SDL_UnlockTexture(get()); }
  inline void destroy() { SDL_DestroyTexture(get()); }
SDLPP_WRAPPER_CLASS_END

// renderer
SDLPP_WRAPPER_CLASS_BEGIN(Renderer, SDL_Renderer)
  typedef SDL_RendererInfo Info;
  typedef SDL_RendererFlip Flip;
  typedef SDL_RendererFlags Flags;

  static inline int getNumDrivers() { return SDL_GetNumRenderDrivers(); }
  static inline result getInfo(int index, Info *info) {
    return SDL_GetRenderDriverInfo(index, info); }

  inline result getInfo(Info *info) { return SDL_GetRendererInfo(get(), info); }
  inline result getOutputSize(int *w, int *h) { return SDL_GetRendererOutputSize(get(), w, h); }
  inline Size getOutputSize() { Size sz; SDL_GetRendererOutputSize(get(), &sz.w, &sz.h); return std::move(sz); }

  inline Texture::Wrapped createTexture(Uint32 format, int access, int w, int h) {
    return Texture::Wrapped(SDL_CreateTexture(get(), format, access, w, h)); }
  inline Texture::Wrapped createTextureFromSurface(SDL_Surface *surface) {
    return Texture::Wrapped(SDL_CreateTextureFromSurface(get(), surface)); }

  inline Bool targetSupported() { return SDL_RenderTargetSupported(get()); } 
  inline result setTarget(SDL_Texture *texture) { return SDL_SetRenderTarget(get(), texture); }
  inline Texture::Wrapped getTarget() { return Texture::Wrapped(SDL_GetRenderTarget(get())); }
  inline result setLogicalSize(int w, int h) { return SDL_RenderSetLogicalSize(get(), w, h); }
  inline void getLogicalSize(int *w, int *h) { SDL_RenderGetLogicalSize(get(), w, h); }
  inline Size getLogicalSize() { Size sz; SDL_RenderGetLogicalSize(get(), &sz.w, &sz.h); return std::move(sz); }
  inline result setIntegerScale(Bool enable) { return SDL_RenderSetIntegerScale(get(), enable); }
  inline Bool getIntegerScale() { return SDL_RenderGetIntegerScale(get()); }
  inline result setViewport(const SDL_Rect *rect) { return SDL_RenderSetViewport(get(), rect); }
  inline void getViewport(SDL_Rect *rect) { SDL_RenderGetViewport(get(), rect); }
  inline Rect getViewport() { Rect rc; SDL_RenderGetViewport(get(), &rc); return std::move(rc); }
  inline result setClipRect(const SDL_Rect *rect) { return SDL_RenderSetClipRect(get(), rect); }
  inline void getClipRect(SDL_Rect *rect) { SDL_RenderGetClipRect(get(), rect); }
  inline Rect getClipRect() { Rect rc; SDL_RenderGetClipRect(get(), &rc); return std::move(rc); }
  inline Bool isClipEnabled() { return SDL_RenderIsClipEnabled(get()); }
  inline result setScale(float scaleX, float scaleY) { return SDL_RenderSetScale(get(), scaleX, scaleY); }
  inline void getScale(float *scaleX, float *scaleY) { return SDL_RenderGetScale(get(), scaleX, scaleY); }
  inline FSize getScale() { FSize sz; SDL_RenderGetScale(get(), &sz.w, &sz.h); return std::move(sz); }
  inline result setDrawColor(Uint8 r, Uint8 g, Uint8 b, Uint8 a) { return SDL_SetRenderDrawColor(get(), r, g, b, a); }
  inline result getDrawColor(Uint8 *r, Uint8 *g, Uint8 *b, Uint8 *a) { return SDL_GetRenderDrawColor(get(), r, g, b, a); }
  inline result setDrawBlendMode(BlendMode blendMode) { return SDL_SetRenderDrawBlendMode(get(), blendMode); }
  inline result getDrawBlendMode(BlendMode *blendMode) { return SDL_GetRenderDrawBlendMode(get(), blendMode); }
  inline result clear() { return SDL_RenderClear(get()); }
  inline result drawPoint(int x, int y) { return SDL_RenderDrawPoint(get(), x, y); }
  inline result drawPoint(const SDL_Point *points, int count) { return SDL_RenderDrawPoints(get(), points, count); }
  inline result drawLine(int x1, int y1, int x2, int y2) { return SDL_RenderDrawLine(get(), x1, y1, x2, y2); }
  inline result drawLine(const SDL_Point *points, int count) { return SDL_RenderDrawLines(get(), points, count); }
  inline result drawRect(const SDL_Rect *rect) { return SDL_RenderDrawRect(get(), rect); }
  inline result drawRect(const SDL_Rect *rects, int count) { return SDL_RenderDrawRects(get(), rects, count); }
  inline result fillRect(const SDL_Rect *rect) { return SDL_RenderFillRect(get(), rect); }
  inline result fillRect(const SDL_Rect *rects, int count) { return SDL_RenderFillRects(get(), rects, count); }
  inline result copy(SDL_Texture *texture, const SDL_Rect *srcrect, const SDL_Rect *dstrect) {
    return SDL_RenderCopy(get(), texture, srcrect, dstrect); }
  inline result copy(SDL_Texture *texture, const SDL_Rect *srcrect, const SDL_Rect *dstrect,
      const double angle, const SDL_Point *center, const Flip flip) {
    return SDL_RenderCopyEx(get(), texture, srcrect, dstrect, angle, center, flip); }
  inline result drawPoint(float x, float y) { return SDL_RenderDrawPointF(get(), x, y); }
  inline result drawPoint(const SDL_FPoint *points, int count) { return SDL_RenderDrawPointsF(get(), points, count); }
  inline result drawLine(float x1, float y1, float x2, float y2) { return SDL_RenderDrawLineF(get(), x1, y1, x2, y2); }
  inline result drawLine(const SDL_FPoint *points, int count) { return SDL_RenderDrawLinesF(get(), points, count); }
  inline result drawRect(const SDL_FRect *rect) { return SDL_RenderDrawRectF(get(), rect); }
  inline result drawRect(const SDL_FRect *rects, int count) { return SDL_RenderDrawRectsF(get(), rects, count); }
  inline result fillRect(const SDL_FRect *rect) { return SDL_RenderFillRectF(get(), rect); }
  inline result fillRect(const SDL_FRect *rects, int count) { return SDL_RenderFillRectsF(get(), rects, count); }
  inline result copy(SDL_Texture *texture, const SDL_Rect *srcrect, const SDL_FRect *dstrect) {
    return SDL_RenderCopyF(get(), texture, srcrect, dstrect); }
  inline result copy(SDL_Texture *texture, const SDL_Rect *srcrect, const SDL_FRect *dstrect,
      const double angle, const SDL_FPoint *center, const Flip flip) {
    return SDL_RenderCopyExF(get(), texture, srcrect, dstrect, angle, center, flip); }
  inline result readPixels(const SDL_Rect *rect, Uint32 format, void *pixels, int pitch) {
    return SDL_RenderReadPixels(get(), rect, format, pixels, pitch); }
  inline void present() { SDL_RenderPresent(get()); }
  inline void destroy() { SDL_DestroyRenderer(get()); }
  inline void* getMetalLayer() { return SDL_RenderGetMetalLayer(get()); }
  inline void* getMetalCommandEncoder() { return SDL_RenderGetMetalCommandEncoder(get()); }
SDLPP_WRAPPER_CLASS_END

// surface
SDLPP_WRAPPER_CLASS_BEGIN(Surface, SDL_Surface)
  static inline Wrapped createRGB(
        Uint32 flags, int width, int height, int depth,
        Uint32 Rmask, Uint32 Gmask, Uint32 Bmask, Uint32 Amask) {
    return Wrapped(
      SDL_CreateRGBSurface(flags, width, height, depth,
          Rmask, Gmask, Bmask, Amask)); }
  static inline Wrapped createRGBWithFormat(
        Uint32 flags, int width, int height, int depth, Uint32 format) {
    return Wrapped(
      SDL_CreateRGBSurfaceWithFormat(flags, width, height, depth, format)); }
  static inline Wrapped createRGBFrom(
        void *pixels, int width, int height, int depth, int pitch,
        Uint32 Rmask, Uint32 Gmask, Uint32 Bmask, Uint32 Amask) {
    return Wrapped(
      SDL_CreateRGBSurfaceFrom(pixels, width, height, depth, pitch,
          Rmask, Gmask, Bmask, Amask)); }
  static inline Wrapped createRGBWithFormatFrom(
        void *pixels, int width, int height, int depth, int pitch, Uint32 format) {
    return Wrapped(
      SDL_CreateRGBSurfaceWithFormatFrom(pixels, width, height, depth, pitch, format)); }
  static inline Wrapped loadBMP(SDL_RWops *src, Bool freesrc) {
    return Wrapped(SDL_LoadBMP_RW(src, freesrc)); }
  static inline Wrapped loadBMP(const char *file) { return Wrapped(SDL_LoadBMP(file)); }

  static inline result convertPixels(int width, int height,
      Uint32 src_format, const void * src, int src_pitch,
      Uint32 dst_format, void * dst, int dst_pitch) {
    return SDL_ConvertPixels(width, height,
      src_format, src, src_pitch,
      dst_format, dst, dst_pitch); }

  inline void free() { SDL_FreeSurface(get()); }
  inline result setPalette(Palette *palette) { return SDL_SetSurfacePalette(get(), palette); }
  inline result lock() { return SDL_LockSurface(get()); }
  inline void unlock() { SDL_UnlockSurface(get()); }
  inline result saveBmp(SDL_RWops *dst, Bool freedst) { return SDL_SaveBMP_RW(get(), dst, freedst); }
  inline result saveBmp(const char *dst) { return SDL_SaveBMP(get(), dst); }
  inline result setRLE(int flag) { return SDL_SetSurfaceRLE(get(), flag); }
  inline result setColorKey(int flag, Uint32 key) { return SDL_SetColorKey(get(), flag, key); }
  inline Bool hasColorKey() { return SDL_HasColorKey(get()); }
  inline result getColorKey(Uint32 *key) { return SDL_GetColorKey(get(), key); }
  inline result setColorMod(Uint8 r, Uint8 g, Uint8 b) { return SDL_SetSurfaceColorMod(get(), r, g, b); }
  inline result getColorMod(Uint8 *r, Uint8 *g, Uint8 *b) { return SDL_GetSurfaceColorMod(get(), r, g, b); }
  inline result setAlphaMod(Uint8 alpha) { return SDL_SetSurfaceAlphaMod(get(), alpha); }
  inline result getAlphaMod(Uint8 *alpha) { return SDL_GetSurfaceAlphaMod(get(), alpha); }
  inline result setBlendMode(BlendMode blendMode) { return SDL_SetSurfaceBlendMode(get(), blendMode); }
  inline result getBlendMode(BlendMode *blendMode) { return SDL_GetSurfaceBlendMode(get(), blendMode); }
  inline Bool setClipRect(const SDL_Rect *rect) { return SDL_SetClipRect(get(), rect); }
  inline void getClipRect(SDL_Rect *rect) { SDL_GetClipRect(get(), rect); }
  inline Rect getClipRect() { Rect rc; SDL_GetClipRect(get(), &rc); return std::move(rc); }

  inline Wrapped duplicate() { return Wrapped(SDL_DuplicateSurface(get())); }
  inline Wrapped convert(const PixelFormat *fmt, Uint32 flags) {
    return Wrapped(SDL_ConvertSurface(get(), fmt, flags)); }
  inline Wrapped convertFormat(Uint32 pixel_format, Uint32 flags) {
    return Wrapped(SDL_ConvertSurfaceFormat(get(), pixel_format, flags)); }

  inline result fillRect(const SDL_Rect *rect, Uint32 color) { return SDL_FillRect(get(), rect, color); }
  inline result fillRects(const SDL_Rect *rects, int count, Uint32 color) { return SDL_FillRects(get(), rects, count, color); }
  inline result blit(SDL_Rect *dstrect, SDL_Surface *src, const SDL_Rect *srcrect) {
    return SDL_BlitSurface(src, srcrect, get(), dstrect); }

  inline Renderer::Wrapped createSoftwareRenderer() {
    return Renderer::Wrapped(SDL_CreateSoftwareRenderer(get())); }
SDLPP_WRAPPER_CLASS_END

// window
SDLPP_WRAPPER_CLASS_BEGIN(Window, SDL_Window)
  typedef SDL_WindowFlags Flags;
  typedef SDL_HitTest HitTest;
  typedef SDL_HitTestResult HitTestResult;

  enum class FullScreen : Uint8 {
    Windowed, Real, Fake
  };

  static inline Wrapped create(const char *title, int x, int y, int w, int h, Uint32 flags) {
    return Wrapped(SDL_CreateWindow(title, x, y, w, h, flags)); }
  static inline Wrapped createFrom(const void *data) { return Wrapped(SDL_CreateWindowFrom(data)); }
  static inline Wrapped getWindowFromID(Uint32 id) { return Wrapped(SDL_GetWindowFromID(id)); }
  static inline Wrapped getGrabbedWindow() { return Wrapped(SDL_GetGrabbedWindow()); }

  inline int getDisplayIndex() { return SDL_GetWindowDisplayIndex(get()); }
  inline result setDisplayMode(const display::Mode *mode) { return SDL_SetWindowDisplayMode(get(), mode); }
  inline result getDisplayMode(display::Mode *mode) { return SDL_GetWindowDisplayMode(get(), mode); }
  inline Uint32 getPixelFormat() { return SDL_GetWindowPixelFormat(get()); }
  inline Uint32 getID() { return SDL_GetWindowID(get()); }
  inline Uint32 getFlags() { return SDL_GetWindowFlags(get()); }
  inline void setTitle(const char *title) { SDL_SetWindowTitle(get(), title); }
  inline const char* getTitle() { return SDL_GetWindowTitle(get()); }
  inline void setIcon(SDL_Surface * icon) { SDL_SetWindowIcon(get(), icon); }
  inline void* setData(const char *name, void *userdata) { return SDL_SetWindowData(get(), name, userdata); }
  inline void* getData(const char *name) { return SDL_GetWindowData(get(), name); }
  inline void setPosition(int x, int y) { SDL_SetWindowPosition(get(), x, y); }
  inline void setPosition(const Point &pt) { SDL_SetWindowPosition(get(), pt.x, pt.y); }
  inline void getPosition(int *x, int *y) { SDL_GetWindowPosition(get(), x, y); }
  inline Point getPosition() { Point pt; SDL_GetWindowPosition(get(), &pt.x, &pt.y); return std::move(pt); }
  inline void setSize(int w, int h) { SDL_SetWindowSize(get(), w, h); }
  inline void setSize(const Size &sz) { SDL_SetWindowSize(get(), sz.w, sz.h); }
  inline void getSize(int *w, int *h) { SDL_GetWindowSize(get(), w, h); }
  inline Size getSize() { Size sz; SDL_GetWindowSize(get(), &sz.w, &sz.h); return std::move(sz); }
  inline result getBordersSize(int *top, int *left, int *bottom, int *right) {
    return SDL_GetWindowBordersSize(get(), top, left, bottom, right); }
  inline void setMinimumSize(int w, int h) { SDL_SetWindowMinimumSize(get(), w, h); }
  inline void getMinimumSize(int *w, int *h) { SDL_GetWindowMinimumSize(get(), w, h); }
  inline Size getMinimumSize() { Size sz; SDL_GetWindowMinimumSize(get(), &sz.w, &sz.h); return std::move(sz); }
  inline void setMaximumSize(int w, int h) { SDL_SetWindowMaximumSize(get(), w, h); }
  inline void getMaximumSize(int *w, int *h) { SDL_GetWindowMaximumSize(get(), w, h); }
  inline Size getMaximumSize() { Size sz; SDL_GetWindowMaximumSize(get(), &sz.w, &sz.h); return std::move(sz); }
  inline void setBordered(Bool bordered) { SDL_SetWindowBordered(get(), bordered); }
  inline void setResizable(Bool resizable) { SDL_SetWindowResizable(get(), resizable); }
  inline void show() { SDL_ShowWindow(get()); }
  inline void hide() { SDL_HideWindow(get()); }
  inline void raise() { SDL_RaiseWindow(get()); }
  inline void maximize() { SDL_MaximizeWindow(get()); }
  inline void minimize() { SDL_MinimizeWindow(get()); }
  inline void restore() { SDL_RestoreWindow(get()); }
  inline result setFullscreen(FullScreen flags) {
    switch (flags) {
      case FullScreen::Windowed: return SDL_SetWindowFullscreen(get(), 0);
      case FullScreen::Real: return SDL_SetWindowFullscreen(get(), SDL_WINDOW_FULLSCREEN);
      case FullScreen::Fake: return SDL_SetWindowFullscreen(get(), SDL_WINDOW_FULLSCREEN_DESKTOP);
    }
    return -1;
  }
  inline Surface::Wrapped getSurface() { return Surface::Wrapped(SDL_GetWindowSurface(get())); }
  inline result updateSurface() { return SDL_UpdateWindowSurface(get()); }
  inline result updateSurfaceRects(const SDL_Rect *rects, int numrects) {
    return SDL_UpdateWindowSurfaceRects(get(), rects, numrects); }
  inline void setGrab(Bool grabbed) { SDL_SetWindowGrab(get(), grabbed); }
  inline Bool isGrab() { return SDL_GetWindowGrab(get()); }
  inline result setBrightness(float brightness) { return SDL_SetWindowBrightness(get(), brightness); }
  inline float getBrightness() { return SDL_GetWindowBrightness(get()); }
  inline result setOpacity(float opacity) { return SDL_SetWindowOpacity(get(), opacity); }
  inline result getOpacity(float *opacity) { return SDL_GetWindowOpacity(get(), opacity); }
  inline float getOpacity() { float opacity = 0.f; SDL_GetWindowOpacity(get(), &opacity); return opacity; }
  inline result setModalFor(SDL_Window *parent) { return SDL_SetWindowModalFor(get(), parent); }
  inline result setInputFocus() { return SDL_SetWindowInputFocus(get()); }
  inline result setGammaRamp(const Uint16 *red, const Uint16 *green, const Uint16 *blue) {
    return SDL_SetWindowGammaRamp(get(), red, green, blue); }
  inline result getGammaRamp(Uint16 *red, Uint16 *green, Uint16 *blue) {
    return SDL_GetWindowGammaRamp(get(), red, green, blue); }
  inline result setHitTest(HitTest callback, void *callback_data) {
    return SDL_SetWindowHitTest(get(), callback, callback_data); }
  inline void destroy() { SDL_DestroyWindow(get()); reset(); }

  inline Renderer::Wrapped createRenderer(int index, Uint32 flags) {
    return Renderer::Wrapped(SDL_CreateRenderer(get(), index, flags)); }
  inline Renderer::Wrapped getRenderer() {
    return Renderer::Wrapped(SDL_GetRenderer(get())); }

  inline Bool isScreenKeyboardShown() { return SDL_IsScreenKeyboardShown(get()); }
SDLPP_WRAPPER_CLASS_END

// events
namespace event {
  typedef SDL_Event Event;
  typedef SDL_EventType Type;
  typedef SDL_CommonEvent CommonEvent;
  typedef SDL_DisplayEvent DisplayEvent;
  typedef SDL_WindowEvent WindowEvent;
  typedef SDL_KeyboardEvent KeyboardEvent;
  typedef SDL_TextEditingEvent TextEditingEvent;
  typedef SDL_TextInputEvent TextInputEvent;
  typedef SDL_MouseMotionEvent MouseMotionEvent;
  typedef SDL_MouseButtonEvent MouseButtonEvent;
  typedef SDL_MouseWheelEvent MouseWheelEvent;
  typedef SDL_JoyAxisEvent JoyAxisEvent;
  typedef SDL_JoyBallEvent JoyBallEvent;
  typedef SDL_JoyHatEvent JoyHatEvent;
  typedef SDL_JoyButtonEvent JoyButtonEvent;
  typedef SDL_JoyDeviceEvent JoyDeviceEvent;
  typedef SDL_ControllerAxisEvent ControllerAxisEvent;
  typedef SDL_ControllerButtonEvent ControllerButtonEvent;
  typedef SDL_ControllerDeviceEvent ControllerDeviceEvent;
  typedef SDL_AudioDeviceEvent AudioDeviceEvent;
  typedef SDL_TouchFingerEvent TouchFingerEvent;
  typedef SDL_MultiGestureEvent MultiGestureEvent;
  typedef SDL_DollarGestureEvent DollarGestureEvent;
  typedef SDL_DropEvent DropEvent;
  typedef SDL_SensorEvent SensorEvent;
  typedef SDL_QuitEvent QuitEvent;
  typedef SDL_OSEvent OSEvent;
  typedef SDL_UserEvent UserEvent;
  typedef SDL_SysWMmsg SysWMmsg;
  typedef SDL_SysWMEvent SysWMEvent;
  typedef SDL_eventaction EventAction;
  typedef SDL_EventFilter EventFilter;

  typedef SDL_WindowEventID WindowEventID;
  typedef SDL_DisplayEventID DisplayEventID;

  enum class EventState : Sint8 {
    Query, Ignore, Disable, Enable
  };

  inline void pump() { SDL_PumpEvents(); }
  inline int peep(Event *events, int numevents, EventAction action
      , Uint32 minType, Uint32 maxType) { 
    return SDL_PeepEvents(events, numevents, action, minType, maxType); }
  inline Bool has(Uint32 type) { return SDL_HasEvent(type); }
  inline Bool has(Uint32 minType, Uint32 maxType) { return SDL_HasEvents(minType, maxType); }
  inline void flush(Uint32 type) { SDL_FlushEvent(type); }
  inline void flush(Uint32 minType, Uint32 maxType) { SDL_FlushEvents(minType, maxType); }
  inline Bool poll(Event *event) { return SDL_PollEvent(event) == 1 ? SDL_TRUE : SDL_FALSE; }
  inline Bool wait(Event *event) { return SDL_WaitEvent(event) == 1 ? SDL_TRUE : SDL_FALSE; }
  inline Bool waitTimeout(Event *event, int ms) { return SDL_WaitEventTimeout(event, ms) == 1 ? SDL_TRUE : SDL_FALSE; }
  inline Bool push(Event *event) { return SDL_PushEvent(event) == 1 ? SDL_TRUE : SDL_FALSE; }
  inline void setFilter(EventFilter filter, void *userdata) { SDL_SetEventFilter(filter, userdata); }
  inline Bool getFilter(EventFilter *filter, void **userdata) { return SDL_GetEventFilter(filter, userdata); }
  inline void addWatch(EventFilter filter, void *userdata) { SDL_AddEventWatch(filter, userdata); }
  inline void delWatch(EventFilter filter, void *userdata) { SDL_DelEventWatch(filter, userdata); }
  inline void filter(EventFilter filter, void *userdata) { SDL_FilterEvents(filter, userdata); }
  inline Uint8 state(Uint32 type, EventState state) {
    int s = SDL_QUERY;
    switch (state) {
      case EventState::Query: s = SDL_QUERY; break;
      case EventState::Ignore: s = SDL_IGNORE; break;
      case EventState::Disable: s = SDL_DISABLE; break;
      case EventState::Enable: s = SDL_ENABLE; break;
    }
    return SDL_EventState(type, s);
  }
  inline Uint8 getState(Uint32 type) { return SDL_EventState(type, SDL_QUERY); }
  inline Uint32 registe(int numevents) { return SDL_RegisterEvents(numevents); }
} // end namespace event

// keyboard
namespace keyboard {
  typedef SDL_Keymod Keymod;
  typedef SDL_Keycode Keycode;
  typedef SDL_Scancode Scancode;

  inline Window::Wrapped getFocusWindow() {
    return Window::Wrapped(SDL_GetKeyboardFocus()); }
  inline const Uint8* getState(int *numkeys) { return SDL_GetKeyboardState(numkeys); }
  inline Keymod getModState() { return SDL_GetModState(); }
  inline void setModState(Keymod modstate) { SDL_SetModState(modstate); }
  inline Keycode getKey(Scancode scancode) { return SDL_GetKeyFromScancode(scancode); }
  inline Keycode getKey(const char *name) { return SDL_GetKeyFromName(name); }
  inline Scancode getScancode(Keymod key) { return SDL_GetScancodeFromKey(key); }
  inline Scancode getScancode(const char* name) { return SDL_GetScancodeFromName(name); }
  inline const char* getScancodeName(Scancode scancode) { return SDL_GetScancodeName(scancode); }
  inline const char* getKeyName(Keymod key) { return SDL_GetKeyName(key); }

  inline void startTextInput() { SDL_StartTextInput(); }
  inline Bool isTextInputActive() { return SDL_IsTextInputActive(); }
  inline void stopTextInput() { SDL_StopTextInput(); }
  inline void setTextInputRect(SDL_Rect *rect) { SDL_SetTextInputRect(rect); }

  inline Bool hasScreenKeyboardSupport() { return SDL_HasScreenKeyboardSupport(); }
  inline Bool isScreenKeyboardShown(SDL_Window *window) { return SDL_IsScreenKeyboardShown(window); }
} // end namespace keyboard

// mouse
namespace mouse {
  typedef SDL_SystemCursor SystemCursor;
  typedef SDL_MouseWheelDirection MouseWheelDirection;

  inline Window::Wrapped getFocusWindow() {
    return Window::Wrapped(SDL_GetMouseFocus()); }
  inline Uint32 getState(int *x, int *y) { return SDL_GetMouseState(x, y); }
  inline Uint32 getGlobalState(int *x, int *y) { return SDL_GetGlobalMouseState(x, y); }
  inline Uint32 getRelativeState(int *x, int *y) { return SDL_GetRelativeMouseState(x, y); }

  inline void warpInWindow(SDL_Window *window, int x, int y) {
    return SDL_WarpMouseInWindow(window, x, y); }
  inline result warpGlobal(int x, int y) { return SDL_WarpMouseGlobal(x, y); }
  inline result setRelativeMode(Bool enabled) {
    return SDL_SetRelativeMouseMode(enabled); }
  inline Bool isRelativeMode() { return SDL_GetRelativeMouseMode(); }
  inline result capture(Bool enabled) {
    return SDL_CaptureMouse(enabled); }

  SDLPP_WRAPPER_CLASS_BEGIN(Cursor, SDL_Cursor)
    static inline Wrapped create(const Uint8 *data, const Uint8 *mask
        , int w, int h, int hot_x, int hot_y) {
      return Wrapped(SDL_CreateCursor(data, mask, w, h, hot_x, hot_y)); }
    static inline Wrapped create(SDL_Surface *surface, int hot_x, int hot_y) {
      return Wrapped(SDL_CreateColorCursor(surface, hot_x, hot_y)); }
    static inline Wrapped create(SystemCursor id) {
      return Wrapped(SDL_CreateSystemCursor(id)); }
    static inline Wrapped get() { return Wrapped(SDL_GetCursor()); }
    static inline Wrapped getDefault() { return Wrapped(SDL_GetDefaultCursor()); }

    static inline void show() { SDL_ShowCursor(1); }
    static inline void hide() { SDL_ShowCursor(0); }
    static inline Bool isShown() { return SDL_ShowCursor(-1) == 0 ? SDL_FALSE : SDL_TRUE; }
    static inline Bool isHidden() { return SDL_ShowCursor(-1) == 0 ? SDL_TRUE : SDL_FALSE; }

    inline void set() { SDL_SetCursor(get()); }
    inline void free() { SDL_FreeCursor(get()); }
  SDLPP_WRAPPER_CLASS_END
} // end namespace mouse

// touch
namespace touch {
  typedef SDL_TouchDeviceType TouchDeviceType;
  typedef SDL_Finger Finger;
  typedef SDL_TouchID TouchID;
  typedef SDL_FingerID FingerID;
  typedef SDL_GestureID GestureID;

  class Touch {
    public:
      Touch(int id) noexcept : id_(id) {}
      inline operator int() const noexcept { return id_; }
      inline operator bool() const noexcept { return id_ >= 0; }
    public:
      inline TouchDeviceType getDeviceType() { return SDL_GetTouchDeviceType(id_); }
      inline int getNumFingers() { return SDL_GetNumTouchFingers(id_); }
      inline Finger* getFinger(int index) { return SDL_GetTouchFinger(id_, index); }
    private:
      TouchID id_;
  };

  inline int getNumDevices() { return SDL_GetNumTouchDevices(); }
  inline Touch getTouch(int index) { return Touch(SDL_GetTouchDevice(index)); }
} // end namespace touch

// haptic
SDLPP_WRAPPER_CLASS_BEGIN(Haptic, SDL_Haptic)
  typedef SDL_HapticEffect HapticEffect;

  class Device {
    public:
      Device(int id) noexcept : id_(id) {}
      inline operator int() const noexcept { return id_; }
      inline operator bool() const noexcept { return id_ >= 0; }
    public:
      inline const char* name() { return SDL_HapticName(id_); }

      inline Haptic::Wrapped open() {
        return Haptic::Wrapped(SDL_HapticOpen(id_)); }
      inline Bool opened() { return SDL_HapticOpened(id_) == 1 ? SDL_TRUE : SDL_FALSE; }
    private:
      int id_;
  };

  static inline int numHaptics() { return SDL_NumHaptics(); }
  static inline Bool mouseIsHaptic() {
    return SDL_MouseIsHaptic() == 0 ? SDL_FALSE : SDL_TRUE; }
  static inline Wrapped openFromMouse() { return Wrapped(SDL_HapticOpenFromMouse()); }
  static inline Bool joystickIsHaptic(SDL_Joystick * joystick) {
    return SDL_JoystickIsHaptic(joystick) == 0 ? SDL_FALSE : SDL_TRUE; }
  static inline Wrapped openFromJoystick(SDL_Joystick * joystick) {
    return Wrapped(SDL_HapticOpenFromJoystick(joystick)); }

  inline int index() { return SDL_HapticIndex(get()); }
  inline Device device() { return Device(SDL_HapticIndex(get())); }
  inline void close() { SDL_HapticClose(get()); }
  inline int numEffects() { return SDL_HapticNumEffects(get()); }
  inline int numEffectsPlaying() { return SDL_HapticNumEffectsPlaying(get()); }
  inline unsigned int query() { return SDL_HapticQuery(get()); }
  inline int numAxes() { return SDL_HapticNumAxes(get()); }

  inline Bool effectSupported(HapticEffect *effect) {
    return SDL_HapticEffectSupported(get(), effect) == 0 ? SDL_FALSE : SDL_FALSE; }
  inline int newEffect(HapticEffect *effect) { return SDL_HapticNewEffect(get(), effect); }
  inline result updateEffect(int effect, HapticEffect *data) {
    return SDL_HapticUpdateEffect(get(), effect, data); }
  inline result runEffect(int effect, Uint32 iterations) {
    return SDL_HapticRunEffect(get(), effect, iterations); }
  inline result stopEffect(int effect) { return SDL_HapticStopEffect(get(), effect); }
  inline void destroyEffect(int effect) { SDL_HapticDestroyEffect(get(), effect); }
  inline int getEffectStatus(int effect) { return SDL_HapticGetEffectStatus(get(), effect); }

  inline result setGain(int gain) { return SDL_HapticSetGain(get(), gain); }
  inline result setAutocenter(Bool autocenter) {
    return SDL_HapticSetAutocenter(get(), autocenter); }

  inline result pause() { return SDL_HapticPause(get()); }
  inline result unpause() { return SDL_HapticUnpause(get()); }
  inline result stopAll() { return SDL_HapticStopAll(get()); }

  inline Bool rumbleSupported() {
    return SDL_HapticRumbleSupported(get()) == 0 ? SDL_FALSE : SDL_FALSE; }
  inline result rumbleInit() { return SDL_HapticRumbleInit(get()); }
  inline result rumblePlay(float strength, Uint32 length) {
    return SDL_HapticRumblePlay(get(), strength, length); }
  inline result rumbleStop() { return SDL_HapticRumbleStop(get()); }
SDLPP_WRAPPER_CLASS_END

// joystick
SDLPP_WRAPPER_CLASS_BEGIN(Joystick, SDL_Joystick)
  typedef SDL_JoystickGUID JoystickGUID;
  typedef SDL_JoystickType JoystickType;
  typedef SDL_JoystickID JoystickID;
  typedef SDL_JoystickPowerLevel JoystickPowerLevel;

  class Device {
    public:
      Device(int id) noexcept : id_(id) {}
      inline operator int() const noexcept { return id_; }
      inline operator bool() const noexcept { return id_ >= 0; }
    public:
      inline const char* getName() {
        return SDL_JoystickNameForIndex(id_); }
      inline int getPlayerIndex() {
        return SDL_JoystickGetDevicePlayerIndex(id_); }
      inline JoystickGUID getGUID() {
        return SDL_JoystickGetDeviceGUID(id_); }
      inline Uint16 getVendor() {
        return SDL_JoystickGetDeviceVendor(id_); }
      inline Uint16 getProduct() {
        return SDL_JoystickGetDeviceProduct(id_); }
      inline Uint16 getProductVersion() {
        return SDL_JoystickGetDeviceProductVersion(id_); }
      inline JoystickType getType() {
        return SDL_JoystickGetDeviceType(id_); }
      inline JoystickID getInstanceID() {
        return SDL_JoystickGetDeviceInstanceID(id_); }
    public:
      inline Joystick::Wrapped open() {
        return Joystick::Wrapped(SDL_JoystickOpen(id_)); }
      static inline Joystick::Wrapped fromInstanceID(JoystickID instance_id) {
        return Joystick::Wrapped(SDL_JoystickFromInstanceID(instance_id)); }
      static inline Joystick::Wrapped fromPlayerIndex(int player_index) {
        return Joystick::Wrapped(SDL_JoystickFromPlayerIndex(player_index)); }
    private:
      int id_;
  };
  
  static inline void lockJoysticks() { SDL_LockJoysticks(); }
  static inline void unlockJoysticks() { SDL_UnlockJoysticks(); }
  static inline int numJoysticks() { return SDL_NumJoysticks(); }
  static inline Device deviceAt(int idx) { return Device(idx); }

  static inline void update() { SDL_JoystickUpdate(); }
  static inline int eventState(int state) { return SDL_JoystickEventState(state); }

  inline const char* name() { return SDL_JoystickName(get()); }
  inline int getPlayerIndex() { return SDL_JoystickGetPlayerIndex(get()); }
  inline void setPlayerIndex(int player_index) {
    return SDL_JoystickSetPlayerIndex(get(), player_index); }
  inline JoystickGUID getGUID() { return SDL_JoystickGetGUID(get()); }
  inline Uint16 getVendor() { return SDL_JoystickGetVendor(get()); }
  inline Uint16 getProduct() { return SDL_JoystickGetProduct(get()); }
  inline Uint16 getProductVersion() { return SDL_JoystickGetProductVersion(get()); }
  inline JoystickType getType() { return SDL_JoystickGetType(get()); }
  inline Bool getAttached() { return SDL_JoystickGetAttached(get()); }
  inline JoystickID instanceID() { return SDL_JoystickInstanceID(get()); }

  inline int numAxes() { return SDL_JoystickNumAxes(get()); }
  inline int numBalls() { return SDL_JoystickNumBalls(get()); }
  inline int numHats() { return SDL_JoystickNumHats(get()); }
  inline int numButtons() { return SDL_JoystickNumButtons(get()); }

  inline Sint16 getAxis(int axis) { return SDL_JoystickGetAxis(get(), axis); }
  inline Bool getAxisInitialState(int axis, Sint16 *state) {
    return SDL_JoystickGetAxisInitialState(get(), axis, state); }
  inline Uint8 getHat(int hat) { return SDL_JoystickGetHat(get(), hat); }
  inline result getBall(int ball, int *dx, int *dy) {
    return SDL_JoystickGetBall(get(), ball, dx, dy); }
  inline Uint8 getButton(int button) { return SDL_JoystickGetButton(get(), button); }
  inline result rumble(Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms) {
    return SDL_JoystickRumble(get(), low_frequency_rumble, high_frequency_rumble, duration_ms); }

  inline void close() { SDL_JoystickClose(get()); }
  inline JoystickPowerLevel currentPowerLevel() { SDL_JoystickCurrentPowerLevel(get()); }

  inline Bool isHaptic() {
    return SDL_JoystickIsHaptic(get()) == 0 ? SDL_FALSE : SDL_TRUE; }
  inline Haptic::Wrapped openHaptic() {
    return Haptic::Wrapped(SDL_HapticOpenFromJoystick(get())); }
  
  static inline void getGUIDString(JoystickGUID guid, char *pszGUID, int cbGUID) {
    return SDL_JoystickGetGUIDString(guid, pszGUID, cbGUID); }
  static inline JoystickGUID getGUIDFromString(const char *pchGUID) {
    return SDL_JoystickGetGUIDFromString(pchGUID); }
SDLPP_WRAPPER_CLASS_END

// hint
namespace hint {
  typedef SDL_HintPriority Priority;
  typedef SDL_HintCallback Callback;

  inline Bool set(const char *name, const char *value, Priority priority) {
    return SDL_SetHintWithPriority(name, value, priority); }
  inline Bool set(const char *name, const char *value) {
    return SDL_SetHint(name, value); }
  inline const char* get(const char *name) { return SDL_GetHint(name); }
  inline Bool get(const char *name, Bool default_value) {
    return SDL_GetHintBoolean(name, default_value); }
  inline void addCallback(const char *name, Callback callback, void *userdata) {
    SDL_AddHintCallback(name, callback, userdata); }
  inline void delCallback(const char *name, Callback callback, void *userdata) {
    SDL_DelHintCallback(name, callback, userdata); }
  inline void clear() { SDL_ClearHints(); }
} // end namespace hint

// display
namespace display {
  inline int getNumVideoDrivers() { return SDL_GetNumVideoDrivers(); }
  inline const char* getVideoDriver(int index) { return SDL_GetVideoDriver(index); }
  inline result videoInit(const char *driver_name) { return SDL_VideoInit(driver_name); }
  inline void videoQuit() { SDL_VideoQuit(); }
  inline const char* getCurrentVideoDriver() { return SDL_GetCurrentVideoDriver(); }
  
  inline int getNum() { return SDL_GetNumVideoDisplays(); }
  inline const char* getName(int displayIndex) { return SDL_GetDisplayName(displayIndex); }
  inline result getBounds(int displayIndex, SDL_Rect *rect) {
    return SDL_GetDisplayBounds(displayIndex, rect); }
  inline result getUsableBounds(int displayIndex, SDL_Rect *rect) {
    return SDL_GetDisplayUsableBounds(displayIndex, rect); }
  inline result getDPI(int displayIndex, float *ddpi, float *hdpi, float *vdpi) {
    return SDL_GetDisplayDPI(displayIndex, ddpi, hdpi, vdpi); }
  inline Orientation getOrientation(int index) { return SDL_GetDisplayOrientation(index); }
  inline int getNumModes(int displayIndex) { return SDL_GetNumDisplayModes(displayIndex); }
  inline result getMode(int displayIndex, int modeIndex, Mode *mode) {
    return SDL_GetDisplayMode(displayIndex, modeIndex, mode); }
  inline result getDesktopMode(int displayIndex, Mode *mode) {
    return SDL_GetDesktopDisplayMode(displayIndex, mode); }
  inline result getCurrentMode(int displayIndex, Mode *mode) {
    return SDL_GetCurrentDisplayMode(displayIndex, mode); }
  inline Mode* getClosestMode(int displayIndex, const Mode *mode, Mode *closest) {
    return SDL_GetClosestDisplayMode(displayIndex, mode, closest); }
} // end namespace display

// screensaver
namespace screensaver {
  inline Bool isEnabled() { return SDL_IsScreenSaverEnabled(); }
  inline void enable() { SDL_EnableScreenSaver(); }
  inline void disable() { SDL_DisableScreenSaver(); }
} // end namespace screensaver

// timer
namespace timer {
  typedef SDL_TimerID TimerID;
  typedef SDL_TimerCallback TimerCallback;

  inline Uint32 getTicks() { return SDL_GetTicks(); }
  inline Bool ticksPassed(Uint32 begin, Uint32 end) { return SDL_TICKS_PASSED(end, begin) ? SDL_TRUE : SDL_FALSE; }
  inline Uint64 getPerformanceCounter() { return SDL_GetPerformanceCounter(); }
  inline Uint64 getPerformanceFrequency() { return SDL_GetPerformanceFrequency(); }
  inline void delay(Uint32 ms) { SDL_Delay(ms); }
  inline TimerID addTimer(Uint32 interval, TimerCallback callback, void *param) {
    return SDL_AddTimer(interval, callback, param); }
  inline Bool removeTimer(TimerID id) { return SDL_RemoveTimer(id); }
} // end namespace timer

// yuv
namespace yuv {
  typedef SDL_YUV_CONVERSION_MODE ConversionMode;

  inline void setConversionMode(ConversionMode mode) { SDL_SetYUVConversionMode(mode); }
  inline ConversionMode getConversionMode() { return SDL_GetYUVConversionMode(); }
  inline ConversionMode getConversionModeForResolution(int width, int height) {
    return SDL_GetYUVConversionModeForResolution(width, height); }
} // end namespace yuv

// clipboard
namespace clipboard {
  inline result setText(const char *text) { return SDL_SetClipboardText(text); }
  inline char* getText() { return SDL_GetClipboardText(); }
  inline Bool hasText() { return SDL_HasClipboardText(); }
} // end namespace clipboard

// gl
namespace gl {
  typedef SDL_GLattr Attr;
  typedef SDL_GLContext Context;
  typedef SDL_GLprofile Profile;
  typedef SDL_GLcontextFlag ContextFlag;
  typedef SDL_GLcontextReleaseFlag ContextReleaseFlag;
  typedef SDL_GLContextResetNotification ContextResetNotification;

  inline result loadLibrary(const char *path) { return SDL_GL_LoadLibrary(path); }
  inline void* getProcAddress(const char *proc) { return SDL_GL_GetProcAddress(proc); }
  inline void unloadLibrary() { return SDL_GL_UnloadLibrary(); }
  inline Bool extensionSupported(const char *extension) { return SDL_GL_ExtensionSupported(extension); }
  inline void resetAttributes() { return SDL_GL_ResetAttributes(); }
  inline result setAttribute(Attr attr, int value) { return SDL_GL_SetAttribute(attr, value); }
  inline result getAttribute(Attr attr, int *value) { return SDL_GL_GetAttribute(attr, value); }
  inline Window::Wrapped getCurrentWindow() { return Window::Wrapped(SDL_GL_GetCurrentWindow()); }
  inline Context getCurrentContext() { return SDL_GL_GetCurrentContext(); }
  inline result setSwapInterval(int interval) { return SDL_GL_SetSwapInterval(interval); }
  inline int getSwapInterval() { return SDL_GL_GetSwapInterval(); }
  inline void deleteContext(Context context) { return SDL_GL_DeleteContext(context); }

  inline result makeCurrent(SDL_Window *window, Context context) { return SDL_GL_MakeCurrent(window, context); }
  inline Context createContext(SDL_Window *window) { return SDL_GL_CreateContext(window); }
  inline void getDrawableSize(SDL_Window *window, int *w, int *h) { SDL_GL_GetDrawableSize(window, w, h); }
  inline Size getDrawableSize(SDL_Window *window) { Size sz; SDL_GL_GetDrawableSize(window, &sz.w, &sz.h); return std::move(sz); }
  inline void swapWindow(SDL_Window *window) { return SDL_GL_SwapWindow(window); }
  
  inline result bindTexture(SDL_Texture *texture, float *texw, float *texh) { return SDL_GL_BindTexture(texture, texw, texh); }
  inline result unbindTexture(SDL_Texture *texture) { return SDL_GL_UnbindTexture(texture); }
} // end namespace gl

// cpuinfo
namespace cpu {
  inline int getCPUCount() { return SDL_GetCPUCount(); }
  inline int getCPUCacheLineSize() { return SDL_GetCPUCacheLineSize(); }
  inline Bool hasRDTSC() { return SDL_HasRDTSC(); }
  inline Bool hasAltiVec() { return SDL_HasAltiVec(); }
  inline Bool hasMMX() { return SDL_HasMMX(); }
  inline Bool has3DNow() { return SDL_Has3DNow(); }
  inline Bool hasSSE() { return SDL_HasSSE(); }
  inline Bool hasSSE2() { return SDL_HasSSE2(); }
  inline Bool hasSSE3() { return SDL_HasSSE3(); }
  inline Bool hasSSE41() { return SDL_HasSSE41(); }
  inline Bool hasSSE42() { return SDL_HasSSE42(); }
  inline Bool hasAVX() { return SDL_HasAVX(); }
  inline Bool hasAVX2() { return SDL_HasAVX2(); }
  inline Bool hasAVX512F() { return SDL_HasAVX512F(); }
  inline Bool hasARMSIMD() { return SDL_HasARMSIMD(); }
  inline Bool hasNEON() { return SDL_HasNEON(); }
  inline int getSystemRAM() { return SDL_GetSystemRAM(); }
  inline size_t SIMDGetAlignment() { return SDL_SIMDGetAlignment(); }
  inline void* SIMDAlloc(const size_t len) { return SDL_SIMDAlloc(len); }
  inline void SIMDFree(void *ptr) { SDL_SIMDFree(ptr); }
} // end namespace cpu

//
inline result init(Uint32 flags) { return SDL_Init(flags); }
inline result initSubSystem(Uint32 flags) { return SDL_InitSubSystem(flags); }
inline void quitSubSystem(Uint32 flags) { return SDL_QuitSubSystem(flags); }
inline Uint32 wasInit(Uint32 flags) { return SDL_WasInit(flags); }
inline void quit() { SDL_Quit(); }

inline result createWindowAndRenderer(int width, int height, Uint32 window_flags,
    Window::Wrapped *window, Renderer::Wrapped *renderer) {
  if (!window || !renderer) return -1;
  SDL_Window *win = nullptr; SDL_Renderer *ren = nullptr;
  auto r = SDL_CreateWindowAndRenderer(width, height, window_flags, &win, &ren);
  if (isSucceeded(r)) {
    (*window).reset(win);
    (*renderer).reset(ren);
  }
  return r;
}

inline BlendMode composeCustomBlendMode(
    BlendFactor srcColorFactor, BlendFactor dstColorFactor, BlendOperation colorOperation,
    BlendFactor srcAlphaFactor, BlendFactor dstAlphaFactor, BlendOperation alphaOperation) {
  return SDL_ComposeCustomBlendMode(srcColorFactor, dstColorFactor, colorOperation,
    srcAlphaFactor, dstAlphaFactor, alphaOperation); }

inline const char* getError() { return SDL_GetError(); }
inline void clearError() { SDL_ClearError(); }

// filepath
inline char* getBasePath() { return SDL_GetBasePath(); }
inline char* getPrefPath(const char *org, const char *app) { return SDL_GetPrefPath(org, app); }

// loadso
SDLPP_WRAPPER_CLASS_BEGIN(Dynamic, void)
  static Wrapped loadObject(const char *sofile) {
    return Wrapped(SDL_LoadObject(sofile)); }
  template <typename T>
  inline T loadFunction(const char *name) {
    return static_cast<T>(SDL_LoadFunction(get(), name)); }
  inline void* loadFunction(const char *name) { return SDL_LoadFunction(get(), name); }
  inline void unloadObject() { SDL_UnloadObject(get()); }
SDLPP_WRAPPER_CLASS_END

#ifdef SDL_IMAGE_VERSION
// SDL_image
namespace img {
  typedef IMG_InitFlags InitFlags;

  inline void version(Version *v) { SDL_IMAGE_VERSION(v); }
  inline Bool init(int flags) { return IMG_Init(flags) != 0 ? SDL_TRUE : SDL_FALSE; }
  inline void quit() { IMG_Quit(); }
  inline Surface::Wrapped load(SDL_RWops *src, Bool freesrc, const char *type) {
    return Surface::Wrapped(IMG_LoadTyped_RW(src, freesrc, type)); }
  inline Surface::Wrapped load(SDL_RWops *src, Bool freesrc) {
    return Surface::Wrapped(IMG_Load_RW(src, freesrc)); }
  inline Surface::Wrapped load(const char *file) {
    return Surface::Wrapped(IMG_Load(file)); }
  inline Texture::Wrapped loadTexture(SDL_Renderer *renderer, const char *file) {
    return Texture::Wrapped(IMG_LoadTexture(renderer, file)); }
  inline Texture::Wrapped loadTexture(SDL_Renderer *renderer, SDL_RWops *src, Bool freesrc) {
    return Texture::Wrapped(IMG_LoadTexture_RW(renderer, src, freesrc)); }
  inline Texture::Wrapped loadTexture(SDL_Renderer *renderer, SDL_RWops *src, Bool freesrc, const char *type) {
    return Texture::Wrapped(IMG_LoadTextureTyped_RW(renderer, src, freesrc, type)); }

  inline Bool isICO(SDL_RWops *src) { return IMG_isICO(src) == 0 ? SDL_FALSE : SDL_TRUE; }
  inline Bool isCUR(SDL_RWops *src) { return IMG_isCUR(src) == 0 ? SDL_FALSE : SDL_TRUE; }
  inline Bool isBMP(SDL_RWops *src) { return IMG_isBMP(src) == 0 ? SDL_FALSE : SDL_TRUE; }
  inline Bool isGIF(SDL_RWops *src) { return IMG_isGIF(src) == 0 ? SDL_FALSE : SDL_TRUE; }
  inline Bool isJPG(SDL_RWops *src) { return IMG_isJPG(src) == 0 ? SDL_FALSE : SDL_TRUE; }
  inline Bool isLBM(SDL_RWops *src) { return IMG_isLBM(src) == 0 ? SDL_FALSE : SDL_TRUE; }
  inline Bool isPCX(SDL_RWops *src) { return IMG_isPCX(src) == 0 ? SDL_FALSE : SDL_TRUE; }
  inline Bool isPNG(SDL_RWops *src) { return IMG_isPNG(src) == 0 ? SDL_FALSE : SDL_TRUE; }
  inline Bool isPNM(SDL_RWops *src) { return IMG_isPNM(src) == 0 ? SDL_FALSE : SDL_TRUE; }
  inline Bool isSVG(SDL_RWops *src) { return IMG_isSVG(src) == 0 ? SDL_FALSE : SDL_TRUE; }
  inline Bool isTIF(SDL_RWops *src) { return IMG_isTIF(src) == 0 ? SDL_FALSE : SDL_TRUE; }
  inline Bool isXCF(SDL_RWops *src) { return IMG_isXCF(src) == 0 ? SDL_FALSE : SDL_TRUE; }
  inline Bool isXPM(SDL_RWops *src) { return IMG_isXPM(src) == 0 ? SDL_FALSE : SDL_TRUE; }
  inline Bool isXV(SDL_RWops *src) { return IMG_isXV(src) == 0 ? SDL_FALSE : SDL_TRUE; }
  inline Bool isWEBP(SDL_RWops *src) { return IMG_isWEBP(src) == 0 ? SDL_FALSE : SDL_TRUE; }

  inline Surface::Wrapped loadICO(SDL_RWops *src) { return Surface::Wrapped(IMG_LoadICO_RW(src)); }
  inline Surface::Wrapped loadCUR(SDL_RWops *src) { return Surface::Wrapped(IMG_LoadCUR_RW(src)); }
  inline Surface::Wrapped loadBMP(SDL_RWops *src) { return Surface::Wrapped(IMG_LoadBMP_RW(src)); }
  inline Surface::Wrapped loadGIF(SDL_RWops *src) { return Surface::Wrapped(IMG_LoadGIF_RW(src)); }
  inline Surface::Wrapped loadJPG(SDL_RWops *src) { return Surface::Wrapped(IMG_LoadJPG_RW(src)); }
  inline Surface::Wrapped loadLBM(SDL_RWops *src) { return Surface::Wrapped(IMG_LoadLBM_RW(src)); }
  inline Surface::Wrapped loadPCX(SDL_RWops *src) { return Surface::Wrapped(IMG_LoadPCX_RW(src)); }
  inline Surface::Wrapped loadPNG(SDL_RWops *src) { return Surface::Wrapped(IMG_LoadPNG_RW(src)); }
  inline Surface::Wrapped loadPNM(SDL_RWops *src) { return Surface::Wrapped(IMG_LoadPNM_RW(src)); }
  inline Surface::Wrapped loadSVG(SDL_RWops *src) { return Surface::Wrapped(IMG_LoadSVG_RW(src)); }
  inline Surface::Wrapped loadTGA(SDL_RWops *src) { return Surface::Wrapped(IMG_LoadTGA_RW(src)); }
  inline Surface::Wrapped loadTIF(SDL_RWops *src) { return Surface::Wrapped(IMG_LoadTIF_RW(src)); }
  inline Surface::Wrapped loadXCF(SDL_RWops *src) { return Surface::Wrapped(IMG_LoadXCF_RW(src)); }
  inline Surface::Wrapped loadXPM(SDL_RWops *src) { return Surface::Wrapped(IMG_LoadXPM_RW(src)); }
  inline Surface::Wrapped loadXV(SDL_RWops *src) { return Surface::Wrapped(IMG_LoadXV_RW(src)); }
  inline Surface::Wrapped loadWEBW(SDL_RWops *src) { return Surface::Wrapped(IMG_LoadWEBP_RW(src)); };
  inline Surface::Wrapped readXPMFromArray(char **xpm) { return Surface::Wrapped(IMG_ReadXPMFromArray(xpm)); };

  inline result savePNG(SDL_Surface *surface, const char *file) {
    return IMG_SavePNG(surface, file); }
  inline result savePNG(SDL_Surface *surface, SDL_RWops *dst, Bool freedst) {
    return IMG_SavePNG_RW(surface, dst, freedst); }
  inline result saveJPG(SDL_Surface *surface, const char *file, int quality) {
    return IMG_SaveJPG(surface, file, quality); }
  inline result saveJPG(SDL_Surface *surface, SDL_RWops *dst, Bool freedst, int quality) {
    return IMG_SaveJPG_RW(surface, dst, freedst, quality); }
} // end namespace img
#endif // end SDL_IMAGE_VERSION

// SDL_ttf
#ifdef SDL_TTF_VERSION
namespace ttf {
  enum class BomType {
    Native, Swapped
  };

  inline void version(Version *v) { SDL_TTF_VERSION(v); }
  inline result init() { return TTF_Init(); }
  inline Bool wasInit() { return TTF_WasInit() == 0 ? SDL_FALSE : SDL_TRUE; }
  inline void quit() { TTF_Quit(); }
  inline void byteSwappedUNICODE(BomType bt) {
    switch (bt) {
      case BomType::Native: TTF_ByteSwappedUNICODE(UNICODE_BOM_NATIVE); break;
      case BomType::Swapped: TTF_ByteSwappedUNICODE(UNICODE_BOM_SWAPPED); break;
    }
  }

  SDLPP_WRAPPER_CLASS_BEGIN(Font, TTF_Font)
    static inline Font::Wrapped open(const char *file, int ptsize) {
      return Font::Wrapped(TTF_OpenFont(file, ptsize)); }
    static inline Font::Wrapped open(const char *file, int ptsize, long index) {
      return Font::Wrapped(TTF_OpenFontIndex(file, ptsize, index)); }
    static inline Font::Wrapped open(SDL_RWops *src, Bool freesrc, int ptsize) {
      return Font::Wrapped(TTF_OpenFontRW(src, freesrc, ptsize)); }
    static inline Font::Wrapped open(SDL_RWops *src, Bool freesrc, int ptsize, long index) {
      return Font::Wrapped(TTF_OpenFontIndexRW(src, freesrc, ptsize, index)); }
    
    inline int getStyle() { return TTF_GetFontStyle(get()); }
    inline void setStyle(int style) { TTF_SetFontStyle(get(), style); }
    inline int getOutline() { return TTF_GetFontOutline(get()); }
    inline void setOutline(int outline) { TTF_SetFontOutline(get(), outline); }
    inline int getHinting() { return TTF_GetFontHinting(get()); }
    inline void setHinting(int hinting) { TTF_SetFontHinting(get(), hinting); }

    inline int height() { return TTF_FontHeight(get()); }
    inline int ascent() { return TTF_FontAscent(get()); }
    inline int descent() { return TTF_FontDescent(get()); }
    inline int lineSkip() { return TTF_FontLineSkip(get()); }
    
    inline Bool getKerning() { return TTF_GetFontKerning(get()) == 0 ? SDL_FALSE : SDL_TRUE; }
    inline void setKerning(Bool allowed) { TTF_SetFontKerning(get(), allowed); }

    inline int faces() { return TTF_FontFaces(get()); }
    inline int faceIsFixedWidth() { return TTF_FontFaceIsFixedWidth(get()); }
    inline char* faceFamilyName() { return TTF_FontFaceFamilyName(get()); }
    inline char* faceStyleName() { return TTF_FontFaceStyleName(get()); }

    inline Bool glyphIsProvided(Uint16 ch) { return TTF_GlyphIsProvided(get(), ch) == 0 ? SDL_FALSE : SDL_TRUE; }
    inline result glyphMetrics(Uint16 ch, int *minx, int *maxx, int *miny, int *maxy, int *advance) {
      return TTF_GlyphMetrics(get(), ch, minx, maxx, miny, maxy, advance); }

    inline int sizeText(const char *text, int *w, int *h) { return TTF_SizeText(get(), text, w, h); }
    inline Size sizeText(const char *text) { Size sz; TTF_SizeText(get(), text, &sz.w, &sz.h); return std::move(sz); }
    inline int sizeUTF8(const char *text, int *w, int *h) { return TTF_SizeUTF8(get(), text, w, h); }
    inline Size sizeUTF8(const char *text) { Size sz; TTF_SizeUTF8(get(), text, &sz.w, &sz.h); return std::move(sz); }
    inline int sizeUNICODE(const Uint16 *text, int *w, int *h) { return TTF_SizeUNICODE(get(), text, w, h); }
    inline Size sizeUNICODE(const Uint16 *text) { Size sz; TTF_SizeUNICODE(get(), text, &sz.w, &sz.h); return std::move(sz); }

    inline Surface::Wrapped renderTextSolid(const char *text, SDL_Color fg) {
      return Surface::Wrapped(TTF_RenderText_Solid(get(), text, fg)); }
    inline Surface::Wrapped renderUTF8Solid(const char *text, SDL_Color fg) {
      return Surface::Wrapped(TTF_RenderUTF8_Solid(get(), text, fg)); }
    inline Surface::Wrapped renderUNICODESolid(const Uint16 *text, SDL_Color fg) {
      return Surface::Wrapped(TTF_RenderUNICODE_Solid(get(), text, fg)); }
    inline Surface::Wrapped renderGlyphSolid(Uint16 ch, SDL_Color fg) {
      return Surface::Wrapped(TTF_RenderGlyph_Solid(get(), ch, fg)); }

    inline Surface::Wrapped renderTextShaded(const char *text, SDL_Color fg, SDL_Color bg) {
      return Surface::Wrapped(TTF_RenderText_Shaded(get(), text, fg, bg)); }
    inline Surface::Wrapped renderUTF8Shaded(const char *text, SDL_Color fg, SDL_Color bg) {
      return Surface::Wrapped(TTF_RenderUTF8_Shaded(get(), text, fg, bg)); }
    inline Surface::Wrapped renderUNICODEShaded(const Uint16 *text, SDL_Color fg, SDL_Color bg) {
      return Surface::Wrapped(TTF_RenderUNICODE_Shaded(get(), text, fg, bg)); }
    inline Surface::Wrapped renderGlyphShaded(Uint16 ch, SDL_Color fg, SDL_Color bg) {
      return Surface::Wrapped(TTF_RenderGlyph_Shaded(get(), ch, fg, bg)); }

    inline Surface::Wrapped renderTextBlended(const char *text, SDL_Color fg) {
      return Surface::Wrapped(TTF_RenderText_Blended(get(), text, fg)); }
    inline Surface::Wrapped renderUTF8Blended(const char *text, SDL_Color fg) {
      return Surface::Wrapped(TTF_RenderUTF8_Blended(get(), text, fg)); }
    inline Surface::Wrapped renderUNICODEBlended(const Uint16 *text, SDL_Color fg) {
      return Surface::Wrapped(TTF_RenderUNICODE_Blended(get(), text, fg)); }
    inline Surface::Wrapped renderGlyphBlended(Uint16 ch, SDL_Color fg) {
      return Surface::Wrapped(TTF_RenderGlyph_Blended(get(), ch, fg)); }

    inline Surface::Wrapped renderTextBlendedWrapped(const char *text, SDL_Color fg, Uint32 wrapLength) {
      return Surface::Wrapped(TTF_RenderText_Blended_Wrapped(get(), text, fg, wrapLength)); }
    inline Surface::Wrapped renderUTF8BlendedWrapped(const char *text, SDL_Color fg, Uint32 wrapLength) {
      return Surface::Wrapped(TTF_RenderUTF8_Blended_Wrapped(get(), text, fg, wrapLength)); }
    inline Surface::Wrapped renderUNICODEBlendedWrapped(const Uint16 *text, SDL_Color fg, Uint32 wrapLength) {
      return Surface::Wrapped(TTF_RenderUNICODE_Blended_Wrapped(get(), text, fg, wrapLength)); }

    inline Surface::Wrapped renderText(const char *text, SDL_Color fg, SDL_Color bg) {
      return Surface::Wrapped(TTF_RenderText(get(), text, fg, bg)); }
    inline Surface::Wrapped renderUTF8(const char *text, SDL_Color fg, SDL_Color bg) {
      return Surface::Wrapped(TTF_RenderUTF8(get(), text, fg, bg)); }
    inline Surface::Wrapped renderUNICODE(const Uint16 *text, SDL_Color fg, SDL_Color bg) {
      return Surface::Wrapped(TTF_RenderUNICODE(get(), text, fg, bg)); }

    inline void close() { TTF_CloseFont(get()); }
    inline int getKerningSizeGlyphs(Uint16 previous_ch, Uint16 ch) {
      return TTF_GetFontKerningSizeGlyphs(get(), previous_ch, ch); }
  SDLPP_WRAPPER_CLASS_END
} // end namespace ttf
#endif // end SDL_TTF_VERSION

// SDL_mixer
#ifdef SDL_MIXER_VERSION
namespace mixer {
  typedef MIX_InitFlags InitFlags;
  typedef Mix_Fading Fading;
  typedef int (SDLCALL *SoundFontCallback)(const char*, void*);
  typedef void (SDLCALL *MixCallback)(void *udata, Uint8 *stream, int len);
  typedef void (SDLCALL *MusicFinishedCallback)(void);
  typedef void (SDLCALL *ChannelFinishedCallback)(int channel);
  typedef Mix_EffectFunc_t EffectFunc;
  typedef Mix_EffectDone_t EffectDone;

  inline void version(Version *v) { SDL_MIXER_VERSION(v); }
  inline Bool init(int flags) { return Mix_Init(flags) != 0 ? SDL_TRUE : SDL_FALSE; }
  inline void quit() { Mix_Quit(); }
  inline result openAudio(int frequency, Uint16 format, int channels, int chunksize) {
    return Mix_OpenAudio(frequency, format, channels, chunksize); }
  inline result openAudioDevice(int frequency, Uint16 format, int channels, int chunksize
      , const char* device, int allowed_changes) {
    return Mix_OpenAudioDevice(frequency, format, channels, chunksize, device, allowed_changes); }
  inline int allocateChannels(int numchans) { return Mix_AllocateChannels(numchans); }
  inline int getAllocatedChannels() { return Mix_AllocateChannels(-1); }
  inline void setAllocateChannels(int numchans) { Mix_AllocateChannels(numchans); }
  inline Bool querySpec(int *frequency, Uint16 *format,int *channels) {
    return Mix_QuerySpec(frequency, format, channels) == 0 ? SDL_FALSE: SDL_TRUE; }

  inline result setSynchroValue(int value) { return Mix_SetSynchroValue(value); }
  inline int getSynchroValue() { return Mix_GetSynchroValue(); }
  inline Bool setSoundFonts(const char *paths) { return
    Mix_SetSoundFonts(paths) == 0 ? SDL_FALSE : SDL_TRUE; }
  inline const char* getSoundFonts() { return Mix_GetSoundFonts(); }
  inline int eachSoundFont(SoundFontCallback callback, void *data) {
    return Mix_EachSoundFont(callback, data); }
  inline void setPostMix(MixCallback callback, void *arg) { Mix_SetPostMix(callback, arg); }

  inline void closeAudio() { Mix_CloseAudio(); }

  class Group;
  class Channel;
  // music
  SDLPP_WRAPPER_CLASS_BEGIN(Music, Mix_Music)
    typedef Mix_MusicType Type;

    static inline int getNumDecoders() { return Mix_GetNumMusicDecoders(); }
    static inline const char* getDecoder(int index) { return Mix_GetMusicDecoder(index); }
    static inline Bool hasDecoder(const char *name) { return Mix_HasMusicDecoder(name); }
    static inline void hook(MixCallback callback, void *arg) { Mix_HookMusic(callback, arg); }
    static inline void hookFinished(MusicFinishedCallback callback) { Mix_HookMusicFinished(callback); }
    static inline void* getHookData() { return Mix_GetMusicHookData(); }

    static inline Wrapped load(const char *file) {
      return Wrapped(Mix_LoadMUS(file)); }
    static inline Wrapped load(SDL_RWops *src, Bool freesrc) {
      return Wrapped(Mix_LoadMUS_RW(src, freesrc)); }
    static inline Wrapped load(SDL_RWops *src, Type type, Bool freesrc) {
      return Wrapped(Mix_LoadMUSType_RW(src, type, freesrc)); }

    inline void free() { Mix_FreeMusic(get()); }
    inline Type getType() { return Mix_GetMusicType(get()); }

    inline result play(int loops) { return Mix_PlayMusic(get(), loops); }
    inline result playFadeIn(int loops, int ms) { return Mix_FadeInMusic(get(), loops, ms); }
    inline result playFadeInPos(int loops, int ms, double position) {
      return Mix_FadeInMusicPos(get(), loops, ms, position); }

    static inline int getVolume() { return Mix_VolumeMusic(-1); }
    static inline void setVolume(int volume) { Mix_VolumeMusic(volume); }
    static inline void halt() { Mix_HaltMusic(); }
    static inline int fadeOut(int ms) { return Mix_FadeOutMusic(ms); }
    static inline Fading fading() { return Mix_FadingMusic(); }

    static inline void pause() { Mix_PauseMusic(); }
    static inline void resume() { Mix_ResumeMusic(); }
    static inline void rewind() { Mix_RewindMusic(); }
    static inline Bool paused() { return Mix_PausedMusic() == 0 ? SDL_FALSE : SDL_TRUE; }

    static inline result setPosition(double position) { return Mix_SetMusicPosition(position); }
    static inline Bool playing() { return Mix_PlayingMusic() == 0 ? SDL_FALSE : SDL_TRUE; }
    static inline void setCMD(const char *command) { Mix_SetMusicCMD(command); }
  SDLPP_WRAPPER_CLASS_END

  // Chunk
  SDLPP_WRAPPER_CLASS_BEGIN(Chunk, Mix_Chunk)
    static inline int getNumDecoders() { return Mix_GetNumChunkDecoders(); }
    static inline const char* getDecoder(int index) { return Mix_GetChunkDecoder(index); }
    static inline Bool hasDecoder(const char *name) { return Mix_HasChunkDecoder(name); }

    static inline Wrapped load(const char *file) {
      return Wrapped(Mix_LoadWAV(file)); }
    static inline Wrapped load(SDL_RWops *src, Bool freesrc) {
      return Wrapped(Mix_LoadWAV_RW(src, freesrc)); }
    static inline Wrapped quickLoad(Uint8 *mem) {
      return Wrapped(Mix_QuickLoad_WAV(mem)); }
    static inline Wrapped quickLoad(Uint8 *mem, Uint32 len) {
      return Wrapped(Mix_QuickLoad_RAW(mem, len)); }

    inline void free() { Mix_FreeChunk(get()); }

    inline Channel play(int loops);
    inline Channel play(int loops, int ticks);
    inline Channel playFadeIn(int loops, int ms);
    inline Channel playFadeIn(int loops, int ms, int ticks);
    inline Bool play(Channel &channel, int loops);
    inline Bool play(Channel &channel, int loops, int ticks);
    inline Bool playFadeIn(Channel &channel, int loops, int ms);
    inline Bool playFadeIn(Channel &channel, int loops, int ms, int ticks);

    inline int getVolume() { return Mix_VolumeChunk(get(), -1); }
    inline void setVolume(int volume) { Mix_VolumeChunk(get(), volume); }
  SDLPP_WRAPPER_CLASS_END
  
  // Channel
  class Channel {
    public:
      Channel(int channel) noexcept : channel_(channel) {}
      inline operator int() const noexcept { return channel_; }
      inline operator bool() const noexcept { return channel_ >= 0; }
    public:
      static inline void hookFinished(ChannelFinishedCallback callback) { Mix_ChannelFinished(callback); }
      static inline int reserveChannels(int num) { return Mix_ReserveChannels(num); }

      inline Bool setPanning(Uint8 left, Uint8 right) {
        return Mix_SetPanning(channel_, left, right) == 0 ? SDL_FALSE : SDL_TRUE; }
      inline Bool setPosition(Sint16 angle, Uint8 distance) {
        return Mix_SetPosition(channel_, angle, distance) == 0 ? SDL_FALSE : SDL_TRUE; }
      inline Bool setDistance(Uint8 distance) {
        return Mix_SetDistance(channel_, distance) == 0 ? SDL_FALSE : SDL_TRUE; }
      inline Bool setReverseStereo(Bool flip) {
        return Mix_SetReverseStereo(channel_, flip) == 0 ? SDL_FALSE : SDL_TRUE; }
      inline Bool setGroup(Group &tag);
      inline Bool clearGroup() {
        return Mix_GroupChannel(channel_, -1) == 0 ? SDL_FALSE : SDL_TRUE; }

      inline Channel play(Mix_Chunk *chunk, int loops) {
        return Channel(Mix_PlayChannel(channel_, chunk, loops)); }
      inline Channel play(Mix_Chunk *chunk, int loops, int ticks) {
        return Channel(Mix_PlayChannelTimed(channel_, chunk, loops, ticks)); }
      inline Channel playFadeIn(Mix_Chunk *chunk, int loops, int ms) {
        return Channel(Mix_FadeInChannel(channel_, chunk, loops, ms)); }
      inline Channel playFadeIn(Mix_Chunk *chunk, int loops, int ms, int ticks) {
        return Channel(Mix_FadeInChannelTimed(channel_, chunk, loops, ms, ticks)); }
      inline int fadeOut(int ms) { return Mix_FadeOutChannel(channel_, ms); }
        
      inline int getVolume() { return Mix_Volume(channel_, -1); }
      inline void setVolume(int volume) { if (*this) Mix_Volume(channel_, volume); }
      static inline int getVolumeAll() { return Mix_Volume(-1, -1); }
      static inline void setVolumeAll(int volume) { Mix_Volume(-1, volume); }

      inline void halt() { Mix_HaltChannel(channel_); }
      inline int expire(int ticks) { return Mix_ExpireChannel(channel_, ticks); }
      inline Fading fading() { return Mix_FadingChannel(channel_); }

      inline void pause() { Mix_Pause(channel_); }
      inline void resume() { Mix_Resume(channel_); }
      inline Bool paused() { return Mix_Paused(channel_) == 0 ? SDL_FALSE : SDL_TRUE; }
      inline Bool playing() { return Mix_Playing(channel_) == 0 ? SDL_FALSE : SDL_TRUE; }
      static inline int playingCount() { return Mix_Playing(-1); }

      inline Chunk::Wrapped chunk() { return Chunk::Wrapped(Mix_GetChunk(channel_)); }

      inline Bool registerEffect(EffectFunc f, EffectDone d, void *arg) {
        return Mix_RegisterEffect(channel_, f, d, arg) == 0 ? SDL_FALSE : SDL_TRUE; }
      inline Bool unregisterEffect(EffectFunc f) {
        return Mix_UnregisterEffect(channel_, f) == 0 ? SDL_FALSE : SDL_TRUE; }
      inline Bool unregisterAllEffects() {
        return Mix_UnregisterAllEffects(channel_) == 0 ? SDL_FALSE : SDL_TRUE; }
    private:
      int channel_;
  };

  // Group
  class Group {
    public:
      Group(int group) noexcept : group_(group) {}
      inline operator int() const noexcept { return group_; }
      inline operator bool() const noexcept { return group_ >= 0; }
    public:
      inline Bool channels(Channel &from, Channel &to) {
        return Mix_GroupChannels(from, to, group_) == 0 ? SDL_FALSE : SDL_TRUE; }
      inline int count() { return Mix_GroupCount(group_);}
      inline Channel firstAvailable() { return Channel(Mix_GroupAvailable(group_)); }
      inline Channel oldest() { return Channel(Mix_GroupOldest(group_)); }
      inline Channel newer() { return Channel(Mix_GroupNewer(group_)); }

      inline void halt() { Mix_HaltGroup(group_); }
      inline int fadeOut(int ms) { return Mix_FadeOutGroup(group_, ms); }
    private:
      int group_;
  };

  // impl
  inline Bool Chunk::play(Channel &channel, int loops) {
    return Mix_PlayChannel(channel, get(), loops) == (int)channel
      ? SDL_TRUE : SDL_FALSE; }
  inline Bool Chunk::play(Channel &channel, int loops, int ticks) {
    return Mix_PlayChannelTimed(channel, get(), loops, ticks) == (int)channel
      ? SDL_TRUE : SDL_FALSE; }
  inline Bool Chunk::playFadeIn(Channel &channel, int loops, int ms) {
    return Mix_FadeInChannel(channel, get(), loops, ms) == (int)channel
      ? SDL_TRUE : SDL_FALSE; }
  inline Bool Chunk::playFadeIn(Channel &channel, int loops, int ms, int ticks) {
    return Mix_FadeInChannelTimed(channel, get(), loops, ms, ticks) == (int)channel
      ? SDL_TRUE : SDL_FALSE; }
  inline Channel Chunk::play(int loops) {
    return Channel(Mix_PlayChannel(-1, get(), loops)); }
  inline Channel Chunk::play(int loops, int ticks) {
    return Channel(Mix_PlayChannelTimed(-1, get(), loops, ticks)); }
  inline Channel Chunk::playFadeIn(int loops, int ms) {
    return Channel(Mix_FadeInChannel(-1, get(), loops, ms)); }
  inline Channel Chunk::playFadeIn(int loops, int ms, int ticks) {
    return Channel(Mix_FadeInChannelTimed(-1, get(), loops, ms, ticks)); }

  inline Bool Channel::setGroup(Group &tag) {
    return Mix_GroupChannel(channel_, tag) == 0 ? SDL_FALSE : SDL_TRUE; }
  
} // end namespace mixer
#endif // end SDL_MIXER_VERSION
} // end namespace sdl

#endif // end __SDL_HPP_